import java.util.Arrays;
import com.skype.Application;
import com.skype.ApplicationAdapter;
import com.skype.Skype;
import com.skype.SkypeException;
import com.skype.Stream;
import com.skype.StreamAdapter;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Date;

class GameUpdater extends Thread {
	
	private RopeState rs;
	private boolean masterrole;
	private SkypeAdapter skype;
	private int sensitivity;

	public GameUpdater(RopeState rs, boolean role, SkypeAdapter skype, int sensitivity) {
		this.rs = rs;
		this.masterrole = role;
		this.skype = skype;
		rs.setConnected(false);
		this.sensitivity = sensitivity;
	}

	public GameUpdater() {
		super();
	}

	public void run() {
		while (true) {
			try {
				if (!rs.isConnected()) {
				//if (disconnected) {
					rs.setConnected(skype.tryConnection());
					//disconnected = !skype.tryConnection();
				} else {

						if (masterrole) { // master updates (if playing) and sends current status
							if (rs.getGameState() == RopeState.PLAYING) {
								// calculate current force for rope
								rs.setRopeForce(rs.getOpponentForce() - rs.getMyForce());
								// calculate current position for rope
								//rs.setRopePosition(rs.getRopePosition() + (rs.getRopeForce() / 10));
								if (this.sensitivity != 0)
									rs.setRopePosition(rs.getRopePosition() + (rs.getRopeForce() / sensitivity));
								else
									rs.setRopePosition(rs.getRopePosition() + (rs.getRopeForce()));
							}
							try { // send rope state
								skype.sendData(rs.toString());
							} catch (Exception e) { 
								System.out.println("Master failed to send data"); 
								rs.setConnected(false);
							}
						} else { // slave sends force
							//System.out.println("Slave sending: " + rs.getMyForce());
							try {
								skype.sendData(Integer.toString(rs.getMyForce() ) );
							} catch (Exception e) { 
								System.out.println("Slave failed to send data"); 
								rs.setConnected(false);
							}
						}
						
						System.out.println(rs);
												
						// check if we are moving to playing state
						if (rs.getGameState() == RopeState.COUNTDOWN && rs.getElapsedTime() > 0) {
							rs.setGameState(RopeState.PLAYING);
						}

						// check for winning conditions
						if ((rs.getRopePosition() > 999 || rs.getRopePosition() < -999) && rs.getGameState() == RopeState.PLAYING) {
							rs.setGameState(RopeState.GAMEOVER);
						}

				} // disconnected
				
				sleep(500);
			} catch (InterruptedException e) { System.out.println(e); }	
		}
	}

	public void startUpdater() {
		this.start();
	}
}

class MyStream extends StreamAdapter {

	private RopeState rs;
	private boolean master;
	private SkypeAdapter skype;

	public MyStream(RopeState newRs, boolean isMaster) {
		this.rs = newRs;
		this.master = isMaster;
	}	

	public MyStream(RopeState newRs, boolean isMaster, SkypeAdapter skype) {
		this.rs = newRs;
		this.master = isMaster;
		this.skype = skype;
	}

	@Override
	public void textReceived(String receivedText) throws SkypeException {
		if (this.master) {
			rs.setOpponentForce(Integer.parseInt(receivedText));
		} else {
			rs.parse(receivedText);
		}
		//System.out.println(receivedText);
	}
}

class SkypeAdapter extends ApplicationAdapter {
	
	Stream streamToUse;
	MyStream streamlistener;
	String opponent;
	Application app;
	private long datasent;

	public SkypeAdapter(RopeState newRs, boolean isMaster, String newOpponent, Application newApp) {
		//streamlistener = new MyStream(newRs, isMaster, this);
		streamlistener = new MyStream(newRs, isMaster);
		this.opponent = newOpponent;
		this.app = newApp;
	}

	public boolean tryConnection() {
		Stream[] tmpStreams;
		System.out.println("Disconnected... Trying to connect with " + this.opponent);
		try {
			tmpStreams = app.connect(this.opponent);
		} catch (SkypeException e) { return false; }
	
		if (tmpStreams.length > 0) {
			return true;
		} else {
			return false;
		}
	}	

	@Override
	public void connected(Stream stream) throws SkypeException {
		printApplicationAndStreamName("connected:", stream);
		streamToUse = stream;
		streamToUse.addStreamListener(streamlistener);
	}

	@Override
	public void disconnected(Stream stream) throws SkypeException {
		printApplicationAndStreamName("disconnected:", stream);
	}

	private void printApplicationAndStreamName(String header, Stream stream) {
		System.out.println(header + stream.getApplication().getName() + "-" + stream.getId());
	}

	public void sendData(String data) throws Exception {
		streamToUse.write(data);
		//System.out.println("OUT: " + data);
	}

	public long getLastDataSent() {
		return this.datasent;	
	}
}

public class TugOfWarClient {
	
	private static void createAndShowGUI(RopeState rs) {
	
		//Create and set up the window.
		JFrame frame = new JFrame("Virtual Tug Of War Client ("+(rs.isMaster()?"Finland":"South Africa")+")");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		//Add the animation panel
		AnimationPanel ap = new AnimationPanel(rs);
		frame.getContentPane().add(ap);

        //Display the window.
		frame.pack();
		frame.setVisible(true);

		//Kick the animation starting
		ap.start();	
	}
	
	public static void main(String[] args) throws Exception {
		// Some variables and objects
		GameUpdater updater;
		boolean master;
		int ropeSensitivity;
		final RopeState rope;
		String name;
		//Stream[] streams;	
		SkypeAdapter skypeapp;
		TugOfWarSensor sensor;
		Application application;
		LocalData localdata;
		Skype.setDebug(false);
		Skype.setDeamon(false);
		name = "TugOfWar";
		if (args.length < 2) {
			System.out.println();
			System.out.println("Usage for Windows:");
			System.out.println(" > tugofwar <opponent's skype id> <master|slave> [<sensitivity>] [<test>]");
			System.out.println("Usage for Linux:");
			System.out.println(" > (sudo) ./tugofwar.sh <opponent's skype id> <master|slave> [<sensitivity>] [<test>]");
 			System.exit(1);
		}
		
		// set whether we are master (Finland)
		if (args[1].equals("master")) {
			master = true;
		} else {
			master = false;
		}
		
		// set the sensitivity of the rope (0 -> very short game, 10 -> very long game)
		if (args.length == 3)
			ropeSensitivity = Integer.parseInt(args[2]);
		else
			ropeSensitivity = 5;
		
		if (master)
			localdata = new LocalData(ropeSensitivity, "0.fi.pool.ntp.org");
		else
			localdata = new LocalData(ropeSensitivity, "ntp1.icomtek.csir.co.za");
		rope = new RopeState(master, localdata);
			
		application = Skype.addApplication(name);
		skypeapp = new SkypeAdapter(rope, master, args[0], application);
		
		application.addApplicationListener(skypeapp);
		
		if (args.length > 3 && args[3].equals("test"))
			sensor = new TugOfWarDummySensor(rope);
		else
			sensor = new TugOfWarPhidgetSensor(rope);
		sensor.initialize();
		
		// set up the GUI
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI(rope); 
			}
		});

		// start up the game updater thread
		updater = new GameUpdater(rope, master, skypeapp, ropeSensitivity);		
		updater.startUpdater();
		
/*
		ik.close();
		ik = null;
		for (Stream stream: streams) {
			stream.write("Disconnecting...");
			stream.disconnect();
		}
		System.out.println(" ok");
		System.exit(0);	
*/
	}
}
